package com.jeesite.modules.digitalize.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.beust.jcommander.internal.Lists;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.modules.digitalize.config.Constants;
import com.jeesite.modules.digitalize.constants.AuditEnum;
import com.jeesite.modules.digitalize.constants.OperationEnum;
import com.jeesite.modules.digitalize.util.response.ResultVo;
import com.jeesite.modules.digitalize.dao.DigitalizePlanDetailDao;
import com.jeesite.modules.digitalize.entity.DigitalizePlanDetail;
import com.jeesite.modules.digitalize.entity.DigitalizeTaskReview;
import com.jeesite.modules.digitalize.entity.dto.DigitalizePlanDetailAddDTO;
import com.jeesite.modules.digitalize.entity.dto.DigitalizePlanTaskUpdateDTO;
import com.jeesite.modules.sys.entity.User;
import com.jeesite.modules.sys.service.UserService;
import com.jeesite.modules.sys.utils.UserUtils;
import org.checkerframework.checker.units.qual.min;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * (DigitalizePlanDetail)表服务实现类
 *
 * @author mxz
 * @since 2023-11-13 17:56:00
 */
@Service
public class DigitalizePlanDetailService extends ServiceImpl<DigitalizePlanDetailDao, DigitalizePlanDetail> {

    @Resource
    DigitalizeTaskReviewService digitalizeTaskReviewService;
    @Resource
    DigitalizePlanTaskUtils digitalizePlanTaskUtils;
    @Resource
    UserService userService;
    @Resource
    DigitalizeTaskService digitalizeTaskService;

    private void setSequence(Long id, Integer sequence) {
        this.lambdaUpdate().set(DigitalizePlanDetail::getSequence, sequence)
                .eq(DigitalizePlanDetail::getId, id).update();
    }
    private ResultVo left(Long id) {
        DigitalizePlanDetail pd = getById(id);
        if (pd == null) {
            return ResultVo.failure("节点计划任务不存在");
        }
        Integer sequence = pd.getSequence();
        Long parentId = pd.getParentId();
        // 1. 顶级节点 不能左移升层级
        if (parentId == null) {
            return ResultVo.failure("顶级任务不能再升级");
        }
        // 2. 非顶级节点升级
        // 2.1 更新本节点的sequence = 父节点的sequence + 1
        DigitalizePlanDetail parent = this.getById(parentId);
        this.lambdaUpdate().set(DigitalizePlanDetail::getSequence, parent.getSequence() + 1)
                .set(DigitalizePlanDetail::getParentId, parent.getParentId())
                .eq(DigitalizePlanDetail::getId, id).update();
        // 2.2 更新本层后续节点的sequence 的父节点为此节点， 且 sequence 都重新排序
        UpdateWrapper wrapper = new UpdateWrapper();
        wrapper.eq("plan_id", pd.getPlanId());
        wrapper.eq("parent_id", parentId);
        wrapper.set("parent_id", id);
        wrapper.setSql("sequence = sequence - " + sequence);
        this.update(wrapper);

        // 3. 更新父节点后续的sequence = 后续节点的sequence + 1
        batchUpdateNextSequence(pd.getPlanId(), parent.getParentId(), parent.getSequence(), 1,  id);

        // 4. 左移 更新父节点时间
        digitalizePlanTaskUtils.updateLeftTime(parentId, id);
        return ResultVo.success();
    }

    private ResultVo right(Long id) {
        DigitalizePlanDetail pd = getById(id);
        if (pd == null) {
            return ResultVo.failure("节点计划任务不存在");
        }
        Integer sequence = pd.getSequence();
        Long parentId = pd.getParentId();
        // 1. 顶级节点 不能左移升层级
        if (sequence == 1) {
            return ResultVo.failure("排序为1的任务不能再降级");
        }
        //  降级为最近父节点的字节点
        DigitalizePlanDetail nearUp = this.lambdaQuery().eq(DigitalizePlanDetail::getParentId, parentId).eq(DigitalizePlanDetail::getSequence, sequence - 1).eq(DigitalizePlanDetail::getPlanId, pd.getPlanId()).one();
        this.lambdaUpdate().eq(DigitalizePlanDetail::getId, id).set(DigitalizePlanDetail::getParentId, nearUp.getId()).set(DigitalizePlanDetail::getSequence, 1).eq(DigitalizePlanDetail::getPlanId, pd.getPlanId()).update();
        // 后续同级节点 sequence 都-1
        batchUpdateNextSequence(pd.getPlanId(), parentId, sequence, -1,  id);

        // 4. 右移 更新父节点时间
        digitalizePlanTaskUtils.updateRightTime(nearUp.getId());
        return ResultVo.success();
    }

    /**
     *
     * @param parentId 父节点id
     * @param sequence 顺序
     * @param change 1 增加  -1 减少
     * @param id 本节点id
     */
    private void batchUpdateNextSequence(Long planId, Long parentId, Integer sequence, Integer change, Long id) {
        UpdateWrapper wrapper = new UpdateWrapper();
        wrapper.eq("plan_id", planId);
        wrapper.eq("parent_id", parentId);
        wrapper.gt("sequence", sequence);
        if (id != null) {
            wrapper.ne("id", id);
        }
        wrapper.setSql(change == 1 ? "sequence = sequence + 1" : "sequence = sequence - 1");
        this.update(wrapper);
    }

    private ResultVo up(Long id) {
        DigitalizePlanDetail pd = getById(id);
        if (pd == null) {
            return ResultVo.failure("节点计划任务不存在");
        }
        Integer sequence = pd.getSequence();
        Long parentId = pd.getParentId();
        // 顶级节点 不操作
        if (sequence == 1) {
            return ResultVo.failure("排序为1的任务不能再上移");
        }
        DigitalizePlanDetail up = this.lambdaQuery().eq(DigitalizePlanDetail::getParentId, parentId).eq(DigitalizePlanDetail::getSequence, sequence - 1).eq(DigitalizePlanDetail::getPlanId, pd.getPlanId()).one();

        // 本层顺序 - 1
        setSequence(id, sequence - 1);
        //  上层顺序 +1
        setSequence(up.getId(), sequence);
        return ResultVo.success();
    }
    private ResultVo down(Long id) {
        DigitalizePlanDetail pd = getById(id);
        if (pd == null) {
            return ResultVo.failure("节点计划任务不存在");
        }
        Integer sequence = pd.getSequence();
        Long parentId = pd.getParentId();
        // 顶级节点 不操作
        DigitalizePlanDetail next = this.lambdaQuery().eq(DigitalizePlanDetail::getParentId, parentId).eq(DigitalizePlanDetail::getSequence, sequence + 1).eq(DigitalizePlanDetail::getPlanId, pd.getPlanId()).one();

        if (next == null) {
            return ResultVo.failure("无后续的任务不能再下移");
        }
        // 本层顺序 + 1
        setSequence(id, sequence + 1);
        //  下层顺序 -1
        setSequence(next.getId(), sequence);

        return ResultVo.success();
    }

    @Transactional(rollbackFor = Exception.class)
    public ResultVo updatePlanTask(DigitalizePlanTaskUpdateDTO updateDTO) {
        OperationEnum command = OperationEnum.getByCommand(updateDTO.getCommand());
        if (command == null) {
            return ResultVo.failure("操作指令错误");
        }
        ResultVo resultVo = ResultVo.success();
        switch (command) {
            case UP:
                resultVo = up(updateDTO.getId());
                break;
            case DOWN:
                resultVo = down(updateDTO.getId());
                break;
            case LEFT:
                resultVo = left(updateDTO.getId());
                break;
            case RIGHT:
                resultVo = right(updateDTO.getId());
                break;
            default:
                resultVo = digitalizePlanTaskUtils.setTime(updateDTO.getId(), command, updateDTO.getDate());
                break;
        }
        return resultVo;
    }

    public DigitalizePlanDetail save(DigitalizePlanDetailAddDTO dto) {
        DigitalizePlanDetail digitalizePlanDetail = BeanUtil.copyProperties(dto, DigitalizePlanDetail.class);
        Integer currentPointId = dto.getCurrentPointId();
        if (currentPointId == null) {
            Integer allSequence = this.lambdaQuery().eq(DigitalizePlanDetail::getParentId, Constants.ROOT_ID).eq(DigitalizePlanDetail::getPlanId, dto.getPlanId()).count();
            digitalizePlanDetail.setSequence(allSequence + 1).setParentId(Constants.ROOT_ID);
        } else {
            DigitalizePlanDetail current = this.getById(currentPointId);
            digitalizePlanDetail.setSequence(current.getSequence());
            digitalizePlanDetail.setParentId(current.getParentId());

            // 3. 更新父节点后续的sequence = 后续节点的sequence + 1
            UpdateWrapper nexWrapper = new UpdateWrapper();
            nexWrapper.eq("parent_id", current.getParentId());
            nexWrapper.eq("plan_id", dto.getPlanId());
            nexWrapper.ge("sequence", current.getSequence());
            nexWrapper.setSql("sequence = sequence + 1");
            this.update(nexWrapper);
        }
        this.save(digitalizePlanDetail);
        return digitalizePlanDetail;
    }

    @Transactional
    public Boolean deletePlanTask(Long id) {
        // 1. 删除本节点 关联的所有任务
        DigitalizePlanDetail planTask = this.getById(id);
        List<DigitalizePlanDetail> dList = getHierarchy(id, planTask.getPlanId()).stream().collect(Collectors.toList());
        dList.add(planTask);
        List<Long> taskIdList = Lists.newArrayList();
        List<Long> dIdList = Lists.newArrayList();
        for (DigitalizePlanDetail digitalizePlanDetail : dList) {
            dIdList.add(digitalizePlanDetail.getId());
            if (digitalizePlanDetail.getTaskId() != null) {
                taskIdList.add(digitalizePlanDetail.getTaskId());
            }
        }
        this.removeByIds(dIdList);
        digitalizeTaskService.delete(taskIdList);
        // 2. 后续节点顺序 -1
        batchUpdateNextSequence(planTask.getPlanId(), planTask.getParentId(), planTask.getSequence(), -1, null);
        return true;
    }
    public List<DigitalizePlanDetail> getHierarchy(Long id, Long planId) {
        List<DigitalizePlanDetail> result = new ArrayList<>();
        List<DigitalizePlanDetail> list = this.lambdaQuery().eq(DigitalizePlanDetail::getPlanId, planId).orderByAsc(DigitalizePlanDetail::getSequence).list();
        suppendUserName(list);
        Map<Long, List<DigitalizePlanDetail>> map = list.stream().collect(Collectors.groupingBy(DigitalizePlanDetail::getParentId));
        traverseHierarchy(id, planId , null, result, map);
        result.sort((o1, o2) -> o1.getLevel().compareTo(o2.getLevel()));
        return result;
    }

    private void suppendUserName(List<DigitalizePlanDetail> list) {
        if (CollectionUtil.isEmpty(list)) return;
        Map<String, User> userMap = new HashMap<>();
        User user1 = UserUtils.getUser();
        list.stream().forEach(item -> item.setUserName(user1.getUserName()));
        digitalizeTaskReviewService.lambdaQuery().select(DigitalizeTaskReview::getReviewerId)
                .eq(DigitalizeTaskReview::getStatus, AuditEnum.AUDITING.getCode())
                .in(DigitalizeTaskReview::getTaskId, list.stream().map(DigitalizePlanDetail::getTaskId).collect(Collectors.toList())).list().forEach(taskReview -> {
            User user = userMap.computeIfAbsent(taskReview.getReviewerId(), userId -> userService.get(userId));
            if (user != null) {
                taskReview.setUserName(user.getUserName());
            }
        });
    }

    private void traverseHierarchy(Long parentId, Long planId, String sequence, List<DigitalizePlanDetail> result, Map<Long, List<DigitalizePlanDetail>> map) {
        List<DigitalizePlanDetail> children = map.get(parentId);
        if (CollectionUtil.isEmpty(children)) {
            return;
        }
        for (DigitalizePlanDetail child : children) {
            String level = sequence != null ? sequence + "," + child.getSequence() : child.getSequence() + "";
            child.setLevel(level);
            result.add(child);
            traverseHierarchy(child.getId(), planId, level, result, map);
        }
    }

    public List<DigitalizePlanDetail> traverseDeep( Long planId) {
        List<DigitalizePlanDetail> list = this.lambdaQuery().eq(DigitalizePlanDetail::getPlanId, planId).orderByAsc(DigitalizePlanDetail::getSequence).list();
        suppendUserName(list);
        Map<Long, List<DigitalizePlanDetail>> map = list.stream().collect(Collectors.groupingBy(DigitalizePlanDetail::getParentId));
        return traverseDeep(Constants.ROOT_ID, map, null);
    }
    public List<DigitalizePlanDetail> traverseDeep(Long parentId, Map<Long, List<DigitalizePlanDetail>> map, String sequence) {
        List<DigitalizePlanDetail> children = map.get(parentId);
        if (CollectionUtil.isEmpty(children)) {
            return children;
        }
        for (DigitalizePlanDetail child : children) {
            String level = sequence != null ? sequence + "," + child.getSequence() : child.getSequence() + "";
            child.setLevel(level);
            child.setChildrenList(traverseDeep(child.getId(), map, level));
            //第一次赋值 (仅为一级时使用  避免存在一级范围比子集范围大太多)
            if (CollectionUtil.isNotEmpty(child.getChildrenList())) {
                DigitalizePlanDetail sub = child.getChildrenList().get(0);
                child.setPlanStartDate(sub.getPlanStartDate());
                child.setPlanEndDate(sub.getPlanEndDate());
                child.setStartDate(sub.getStartDate());
                child.setEndDate(sub.getEndDate());
            }
            int i = traverseDeepDate(child.getId(), map, child);
            if (i == 0) {
                i = getDuration(child.getStartDate(), child.getEndDate());
            }
            child.setDuration(i);
        }
        return children;
    }

    private int traverseDeepDate(Long parentId, Map<Long, List<DigitalizePlanDetail>> map,
                                    DigitalizePlanDetail child) {
        List<DigitalizePlanDetail> children = map.get(parentId);
        if (CollectionUtil.isEmpty(children)) {
            return 0;
        }
        Date planMax = child.getPlanEndDate();
        Date planMin = child.getPlanStartDate();
        Date max = child.getEndDate();
        Date min = child.getStartDate();
        int count = 0;
        for (DigitalizePlanDetail nextChild : children) {
            planMin = timeCompare(nextChild.getPlanStartDate(), planMin, true);
            planMax = timeCompare(nextChild.getPlanEndDate(), planMax, false);
            min = timeCompare(nextChild.getStartDate(), min, true);
            max = timeCompare(nextChild.getEndDate(), max, false);
            int i = traverseDeepDate(nextChild.getId(), map, nextChild);
            if (i == 0) {
                i = getDuration(nextChild.getStartDate(), nextChild.getEndDate());
            }
            nextChild.setDuration(i);
            count = count + i;
        }
        child.setPlanEndDate(planMax);
        child.setPlanStartDate(planMin);
        child.setEndDate(max);
        child.setStartDate(min);
        return count;
    }

    private Date timeCompare(Date time1, Date time2, boolean flag) {
        if (null == time1) {
            return time2;
        } else if (null == time2) {
            return time1;
        } else {
            if(flag){
                return time1.before(time2) ? time1 : time2;
            }else {
                return time1.after(time2) ? time1 : time2;
            }
        }
    }


    private Integer getDuration(Date start, Date end) {
        //格式化时间  yyyy-MM-dd HH:mm:ss -> yyyy-MM-dd
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            if (start == null || end == null) {
                return 0;
            }
            Date startTime = sdf.parse(sdf.format(start));
            Date endTime = sdf.parse(sdf.format(end));
            long l = endTime.getTime() - startTime.getTime();
            if (l == 0L) {
                return 1;
            } else {
                return (int) (l / 1000 / 3600 / 24) + 1;
            }
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }

    public String getComponentListByPlanDetailId(Long id) {
        List<String> componentIdList = this.baseMapper.getComponentListByPlanDetailId(id);
        if(CollectionUtil.isEmpty(componentIdList)){
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (String idList : componentIdList) {
            if (StringUtils.isNotEmpty(idList)) {
                //["2315","2314"]  转换
                String replace = idList.replace("]", "").replace("[", "");
                if (StringUtils.isEmpty(sb)) {
                    sb.append(replace);
                } else {
                    sb.append(",").append(replace);
                }
            }
        }
        return sb.toString().replaceAll("\"","");
    }
}

